Skip to content

Restricted access - #756

Open
kernicPanel wants to merge 22 commits into
mainfrom
restricted-access
Open

Restricted access#756
kernicPanel wants to merge 22 commits into
mainfrom
restricted-access

Conversation

@kernicPanel

@kernicPanel kernicPanel commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Purpose

Closes #731.

Let a folder owner restrict a folder so that only its explicit members
keep access, cutting the roles and link reach inherited from the tree.

This replaces the previous approach of this PR (cutting inheritance with
query-level exclusions) following review: the boundary had to be
reimplemented in every access-related query, and any forgotten spot
leaked content — link reach was already missing from the visibility
filters. The branch was rebuilt from main.

Proposal

A restricted folder physically becomes a root of the tree, so
inheritance stops by construction and every existing and future query
is correct without knowing about restriction. A new shortcut item
type materializes its original location.

  • is_restricted field and shortcut item type targeting a restricted root
  • restrict ability: explicit owners only, activation needs a parent, deactivation always possible
  • activation: explicit owner access, move to the root, shortcut left in place, explicit link reach kept (defaulted to restricted when inherited)
  • deactivation: reattach at the shortcut's current parent, then normalize explicit accesses and link reach against restored inheritance
  • API: PATCH {"is_restricted": bool}, target payload on shortcuts (can_access drives the greyed entry), shortcuts in the tree endpoint
  • top-level listing hides a restricted root from users who already reach its live shortcut
  • deleting a shortcut detaches the folder (the excluded owner acts on the container only); trashing an ancestor or the restricted folder itself detaches too
  • shortcuts excluded from search, export and indexing
  • a folder can be created restricted (parent owners only)
  • permissions refactor: role resolution and abilities behind a swappable backend
  • fix: parent() resolved by exact path after a move

Frontend follow-up needed: render the shortcut type (open target.id
when can_access, greyed otherwise) and the activation/deactivation
warnings.

@kernicPanel
kernicPanel force-pushed the restricted-access branch 7 times, most recently from e8d21cc to 001fd58 Compare July 6, 2026 13:24
kernicPanel added a commit that referenced this pull request Jul 6, 2026
The soft delete assertion sat inside the pytest.raises block and
never ran, hiding a wrong expected message. Indexer error tests
now keep a single raising invocation inside the block so the
failure source is unambiguous. Flagged by Sonar on PR #756.
kernicPanel added a commit that referenced this pull request Jul 6, 2026
The soft delete assertion sat inside the pytest.raises block and
never ran, hiding a wrong expected message. Indexer error tests
now keep a single raising invocation inside the block so the
failure source is unambiguous. Flagged by Sonar on PR #756.
kernicPanel added a commit that referenced this pull request Jul 6, 2026
The soft delete assertion sat inside the pytest.raises block and
never ran, hiding a wrong expected message. Indexer error tests
now keep a single raising invocation inside the block so the
failure source is unambiguous. Flagged by Sonar on PR #756.
@kernicPanel
kernicPanel requested review from NathanVss and lunika July 6, 2026 16:32
@kernicPanel
kernicPanel marked this pull request as ready for review July 6, 2026 16:33
kernicPanel added a commit that referenced this pull request Jul 6, 2026
The soft delete assertion sat inside the pytest.raises block and
never ran, hiding a wrong expected message. Indexer error tests
now keep a single raising invocation inside the block so the
failure source is unambiguous. Flagged by Sonar on PR #756.
kernicPanel added a commit that referenced this pull request Jul 7, 2026
The soft delete assertion sat inside the pytest.raises block and
never ran, hiding a wrong expected message. Indexer error tests
now keep a single raising invocation inside the block so the
failure source is unambiguous. Flagged by Sonar on PR #756.
@kernicPanel
kernicPanel force-pushed the restricted-access branch 2 times, most recently from ee6b94a to 8d0b54d Compare July 7, 2026 14:10
kernicPanel added a commit that referenced this pull request Jul 7, 2026
The soft delete assertion sat inside the pytest.raises block and
never ran, hiding a wrong expected message. Indexer error tests
now keep a single raising invocation inside the block so the
failure source is unambiguous. Flagged by Sonar on PR #756.
kernicPanel added a commit that referenced this pull request Jul 7, 2026
The soft delete assertion sat inside the pytest.raises block and
never ran, hiding a wrong expected message. Indexer error tests
now keep a single raising invocation inside the block so the
failure source is unambiguous. Flagged by Sonar on PR #756.
kernicPanel added a commit that referenced this pull request Jul 7, 2026
The soft delete assertion sat inside the pytest.raises block and
never ran, hiding a wrong expected message. Indexer error tests
now keep a single raising invocation inside the block so the
failure source is unambiguous. Flagged by Sonar on PR #756.
@kernicPanel
kernicPanel marked this pull request as draft July 7, 2026 16:32
@lterm lterm moved this from To do to In Progress in La Suite Drive Jul 13, 2026
@lterm

lterm commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Thanks @sampaccoud for the review. So the alternative is to move a restricted folder to root, with a "restriction link" placeholder marking its original spot.

A few UX/UI questions this raises:

  • This restriction link ties directly into the shortcuts feature : should we plan a specific representation for links to restricted folders, distinct from regular shortcuts?
  • What does the flow look like when a user activates/deactivates the restriction ? do we walk them through what's about to happen (e.g. an info message or confirmation step explaining the folder will be moved to root)?
  • What happens when someone tries to delete a restricted folder they don't have access to?

@robin-lecomte, what do you think?

@sampaccoud

sampaccoud commented Jul 13, 2026

Copy link
Copy Markdown
Member
  • This restriction link ties directly into the shortcuts feature : should we plan a specific representation for links to restricted folders, distinct from regular shortcuts?

I could be a separate object or a shortcut flagged as "restriction" 🤔

  • What does the flow look like when a user activates/deactivates the restriction ? do we walk them through what's about to happen (e.g. an info message or confirmation step explaining the folder will be moved to root)?

You don't need to tell them about moving the item to the root (plus it will be hidden from the root..)
You probably need to explain something when a restriction is activated: that the item's permissions are going to be detached from the above tree structure and that access rights inheritance will not apply anymore. Maybe let them know that this can be reverted?
Same when deactivating, warn them that access rights will now inherit from the tree structure in which the item is located...

The reason for informing the user is that access rights on the item are changing, not that we move the item to/from the root (not his business and only a technical subject)

  • What happens when someone tries to delete a restricted folder they don't have access to?

This was discussed elsewhere in my review with several cases:

  • the user is NOT owner of the folder containing the restricted item :
    • he can only see the restricted item if he has read access to it.
    • he can only delete the restricted item if he is a owner on it.
  • the user is owner of the containing folder :
    • he can see the restricted item even without read access to it (in this case it is grey and can't be entered into)
    • he can only delete the restricted item if he is a owner on it. Eventually, he can manually detach it for good before being allowed to delete the containing folder... In the current version of the PR, this issue was solved by moving the restricted folder in order to be able to delete the containing folder... With the pattern I propose, detachment is already done and we only have to delete the restriction shortcut and reset the is_restricted flag on the now root folder ... easy!

Allow login with demo users in local development.
Move role and access resolution behind a backend resolved from
the new PERMISSIONS_BACKEND setting, following the entitlements
backend convention. Behavior is unchanged; this gives a single
decision point to swap for an ABAC engine later.
Pure move of Item.get_abilities into the role backend so every
permission decision sits behind the same facade.
Each ability now reads as a named rule on ItemAbilities, fixing
the Sonar S3776 complexity of the former monolithic function.
Per-action properties also sketch the vocabulary a future ABAC
engine will implement, one check per action.
Sonar S3516 flags validate() because every return yields the
same attrs value. Merge the restricted early return into an
if/else with a single exit and move the ancestors validation
to a helper to keep cognitive complexity low; behavior is
unchanged.
The soft delete assertion sat inside the pytest.raises block and
never ran, hiding a wrong expected message. Indexer error tests
now keep a single raising invocation inside the block so the
failure source is unambiguous. Flagged by Sonar on PR #756.
TreeModel.parent() uses .last() which follows Meta.ordering
(created_at). After a move(), the grandparent created later
is returned instead of the direct parent. The parent path is
known statically, so look it up by equality instead of
scanning ancestors.
Allow folders to be marked as restricted. A DB constraint
prevents setting is_restricted on non-folder items.
A shortcut materializes the original location of a restricted
folder moved to the tree root. The OneToOne target enforces a
single shortcut per folder and a DB constraint ties the target
to the shortcut type.
Only an explicit owner can toggle restriction on a folder. A folder
needs a parent to host its shortcut on activation, while an already
restricted folder lives at the tree root and must stay deactivatable.
Restriction is structural: the folder physically leaves its parent
so inheritance stops applying without any query-level cut. A
shortcut materializes its origin location. Explicit link reach is
kept and defaults to restricted only when it was inherited.
The folder returns under its shortcut's current parent and the
shortcut disappears. Without a live shortcut the folder stays a
detached root. Inheritance applies again through the tree structure.
Explicit roles granted during restriction that are now covered by
inheritance are dropped, so the sharing screen does not keep dead
entries. Superior roles and roles without inherited counterpart stay.
The reach kept from the restriction period is reset to inherit when
the reattached parent already grants as much or more. A more open
explicit reach survives, matching the role normalization rule.
Owners activate and deactivate restriction on folders via PATCH,
gated by the restrict ability. The serializer keeps working on the
instance returned by the toggle since the item physically moves.
Shortcuts expose their target's id, title and a can_access flag so
the frontend can grey out entries pointing to folders the user
cannot open. The children listing prefetches the viewer accesses to
keep the query count flat, and the tree includes shortcut entries.
A restricted folder lives at the tree root but its members reach it
through the shortcut when they can open the containing folder. The
listing hides the root in that case so the folder shows up in a
single location, and keeps it for members without container access.
Deleting a shortcut removes the entry from the containing folder
without trashing anything. An owner excluded from the target acts
on the container only and can neither destroy, declassify nor read
it: the folder keeps its accesses, stays restricted, and surfaces
in its members' top-level listing.
Trashing a folder must not drag restricted folders with it: their
shortcuts are removed before the subtree is marked deleted, so each
target survives untouched for its members while the rest of the
branch goes to the trash as usual. Restoring the ancestor does not
bring the shortcuts back.
An explicit owner trashing a restricted folder leaves no entry
pointing into the trash. The folder restores as a detached root,
still restricted, reachable by its members from their listing.
Shortcuts are tree entries, not content: they never match a search,
never reach the search index, and leave no entry in an exported
archive. The target itself is indexed and exported through its own
root, so users excluded from a restricted folder cannot find its
content through search or an ancestor export.
Creating a child folder with is_restricted chains creation and
activation atomically, so the folder starts as a restricted root
with its shortcut in place. Only owners of the parent may use it:
a lower role would promote itself to owner through restriction.
Shortcuts themselves can never be created directly.
@sonarqubecloud

Copy link
Copy Markdown

@kernicPanel
kernicPanel requested a review from sampaccoud July 28, 2026 15:53
@kernicPanel

Copy link
Copy Markdown
Collaborator Author

@sampaccoud @lterm
I reworked this PR with the suggested approach.
I updated the description accordingly.

As most inline threads target code that no longer exists, a few comments need answers:

  • Restriction stays folder-only: This is part of the rules found in specs, but it can be changed if needed.
  • Explicit link reach is preserved on activation: Also a spec rule
  • Deactivation only cleans up the folder's own explicit accesses. Values set on its subfolders and files during restriction are kept, they were set on purpose. Warning the user about restored inheritance belongs to the frontend flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

Restricted folders

3 participants